/*******************************************************************************
 * Copyright 2020 huanggefan.cn
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/

package main

import (
    "context"
    "flag"
    "log"
    "net"
    "net/http"
    "runtime"
    "sync"
    "time"

    "gitee.com/WisdomClassroom/DisciplineStruct/glb"
    "gitee.com/WisdomClassroom/DisciplineStruct/service"
    "gitee.com/WisdomClassroom/core"
    "gitee.com/WisdomClassroom/core/models"
    "gitee.com/WisdomClassroom/core/protobufs/v1/pb"
    grpcRuntime "github.com/grpc-ecosystem/grpc-gateway/runtime"
    "github.com/jinzhu/gorm"
    _ "github.com/jinzhu/gorm/dialects/postgres"
    _ "github.com/jinzhu/gorm/dialects/sqlite"
    "google.golang.org/grpc"
)

var (
    BuildTag  string
    BuildGo   string
    BuildTime string
)

func runGateway(wg *sync.WaitGroup) {
    defer wg.Done()

    headerMatcher := func(key string) (string, bool) {
        return key, true
    }

    gateway := grpcRuntime.NewServeMux(grpcRuntime.WithIncomingHeaderMatcher(headerMatcher),
        grpcRuntime.WithMarshalerOption(grpcRuntime.MIMEWildcard,
            &grpcRuntime.JSONPb{OrigName: true, EmitDefaults: true}))
    opts := []grpc.DialOption{grpc.WithInsecure()}
    err := pb.RegisterDisciplineStructServiceHandlerFromEndpoint(context.Background(), gateway, core.FlagGRPCListen, opts)
    if err != nil {
        glb.Logger.Error(err.Error())
        return
    }

    mux := http.NewServeMux()
    mux.Handle("/api/", gateway)

    err = http.ListenAndServe(core.FlagHttpListen, mux)
    if err != nil {
        glb.Logger.Error(err.Error())
        return
    }
}

func runServer(wg *sync.WaitGroup) {
    defer wg.Done()

    listen, err := net.Listen("tcp", core.FlagGRPCListen)
    if err != nil {
        glb.Logger.Error(err.Error())
        return
    }

    grpcServer := grpc.NewServer()
    pb.RegisterDisciplineStructServiceServer(grpcServer, &service.Service{})

    if err = grpcServer.Serve(listen); err != nil {
        glb.Logger.Error(err.Error())
        return
    }
}

func main() {
    core.PublicFlag(BuildTag, BuildGo, BuildTime)
    core.PublicServerFlag()
    flag.Parse()

    if core.FlagHelp {
        flag.Usage()
        return
    }
    if core.FlagVersion {
        core.PublicVersion()
        return
    }
    if core.FlagUseSqlite == core.FlagUsePG {
        log.Print("You must choose one database.\n\n")
        flag.Usage()
        return
    }

    if core.FlagLogWithUDP {
        addr, err := net.ResolveUDPAddr("udp", core.FlagLogAddr)
        if err != nil {
            log.Fatalln(err)
        }
        glb.Logger, err = core.NewUDPLogger(core.FlagLogLevel, addr, 10240)
        if err != nil {
            log.Fatalln(err)
        }
    } else {
        glb.Logger = core.NewLogger(core.FlagLogLevel)
    }

    if core.FlagPPROFEnable {
        go core.StartPPOFDebug(http.Server{Addr: core.FlagPPROFListen}, true)
        time.Sleep(1 * time.Second)
    }

    glb.Logger.Info("http listen at: http://" + core.FlagHttpListen)
    glb.Logger.Info("grpc listen at: grpc://" + core.FlagGRPCListen)
    if core.FlagLogWithUDP {
        glb.Logger.Info("log to: udp://" + core.FlagLogAddr)
    }

    var err error
    if core.FlagUseSqlite {
        glb.DB, err = gorm.Open("sqlite3", core.FlagSqliteDB)
    } else {
        glb.DB, err = gorm.Open("postgres", models.GetPgSQLConf())
    }
    if err != nil {
        log.Fatalln(err)
    }
    defer func() {
        err = glb.DB.Close()
        if err != nil {
            glb.Logger.Error(err.Error())
        }
    }()

    wg := new(sync.WaitGroup)
    wg.Add(2)
    go runGateway(wg)
    go runServer(wg)
    wg.Wait()
}

func init() {
    runtime.GOMAXPROCS(1)
}
